﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Runtime.Serialization.Formatters.Binary;
using System.Windows.Forms;
using System.IO;

namespace SimpleRest.Tools.Scaffolding
{
    public partial class Form1 : Form
    {
        private List<string> Primitives { get; set; }
        private Dictionary<string, string> SqlConversionTypes
        {
            get;
            set;
        }

        public Form1()
        {
            InitializeComponent();

            Primitives = new List<string>()
            {
                typeof(string).FullName,
                typeof(Int16).FullName,
                typeof(Int32).FullName,
                typeof(Int64).FullName,
                typeof(UInt16).FullName,
                typeof(UInt32).FullName,
                typeof(UInt64).FullName,
                typeof(double).FullName,
                typeof(float).FullName,
                typeof(decimal).FullName,
                typeof(DateTime).FullName,
                typeof(Guid).FullName,
                typeof(char).FullName,
                typeof(bool).FullName
            };

            SqlConversionTypes = new Dictionary<string, string>()
            {
                {typeof(string).FullName, "NVARCHAR(MAX)"},
                {typeof(Int16).FullName, "SMALLINT"},
                {typeof(Int32).FullName, "INT"},
                {typeof(Int64).FullName, "BIGINT"},
                {typeof(UInt16).FullName, "INT"},
                {typeof(UInt32).FullName, "INT"},
                {typeof(UInt64).FullName, "BIGINT"},
                {typeof(double).FullName, "DECIMAL(18, 10)"},
                {typeof(float).FullName, "DECIMAL(18, 10)"},
                {typeof(decimal).FullName, "DECIMAL(18, 10)"},
                {typeof(DateTime).FullName, "DATETIME"},
                {typeof(Guid).FullName, "UNIQUEIDENTIFIER"},
                {typeof(char).FullName, "CHAR(1)"},
                {typeof(bool).FullName, "BIT"},
            };

            AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
            AppDomain.CurrentDomain.ResourceResolve += new ResolveEventHandler(CurrentDomain_ResourceResolve);
            AppDomain.CurrentDomain.TypeResolve += new ResolveEventHandler(CurrentDomain_TypeResolve);
            this.Load += new EventHandler(Form1_Load);
        }

        Assembly CurrentDomain_TypeResolve(object sender, ResolveEventArgs args)
        {
            throw new NotImplementedException();
        }

        Assembly CurrentDomain_ResourceResolve(object sender, ResolveEventArgs args)
        {
            throw new NotImplementedException();
        }

        Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
        {
            var a = AppDomain.CurrentDomain.GetAssemblies().First(d => d.FullName == args.RequestingAssembly.FullName);
            return a;
        }

        void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
        {
        }

        void Form1_Load(object sender, EventArgs e)
        {
            
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenFileDialog dialog = new OpenFileDialog();
            dialog.Filter = "Assembilies (dll's)|*dll|Exes (exe's)|*.exe";
            dialog.RestoreDirectory = true;

            List<Type> types = new List<Type>();

            if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                foreach (string name in dialog.FileNames)
                {
                    var split = name.Split(new char[] { '\\' });
                    string directoryName = string.Empty;
                    for (int i = 0; i < split.Length - 1; i++)
                        directoryName += split[i] + "/";

                    System.Reflection.Assembly assembly = null;
                    try
                    {
                        foreach (var file in new DirectoryInfo(directoryName).GetFiles("*.dll"))
                        {
                            AppDomain.CurrentDomain.Load(File.ReadAllBytes(file.FullName));
                        }
                        assembly = Assembly.ReflectionOnlyLoadFrom(name);
                        assembly = AppDomain.CurrentDomain.GetAssemblies().First(d => d.FullName == assembly.FullName);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("Failed to load assembly: " + name + "\n" + ex.ToString());
                    }

                   //Assembly.ReflectionOnlyLoadFrom(name)
                    if (assembly != null)
                    {
                        bool showErrors = true;
                        var allTypes = assembly.GetTypes();
                        textBox3.Text = allTypes.First().Namespace;
                        foreach (var type in allTypes)
                        {
                            try
                            {
                                types.Add(type);
                            }
                            catch (Exception ex)
                            {
                            }
                        }
                    }
                }

                textBox1.Text = "";
                foreach (var file in dialog.FileNames)
                    textBox1.Text += file + ";";
                foreach (var type in types.OrderBy(d => d.FullName))
                    listBox1.Items.Add(type);
            }
        }

        private byte[] ToArray(Assembly assembly)
        {
            byte[] dllAsArray;

            using (MemoryStream ms = new MemoryStream())
            {
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(ms, assembly);
                dllAsArray = ms.ToArray();
            }
            return dllAsArray;
        }

        private ObjectAssociationGraph<Type> Graph = new ObjectAssociationGraph<Type>();
        private List<string> FilesCreated = new List<string>();

        private void BuildGraph(Type type)
        {
            if (type == null)
                return;
            foreach (var t in type.GetProperties())
            {
                if (!this.Primitives.Contains(t.PropertyType.FullName)) // complex type
                {
                    if (t.PropertyType.IsGenericType)
                    {
                        foreach (Type gType in t.PropertyType.GetGenericArguments())
                        {
                            if (!Primitives.Contains(gType.FullName)) // 1 deep
                            {
                                this.Graph.Add(new ObjectAssociationNode<Type>()
                                {
                                    TargetNode = gType,
                                    DestinationNode = type,
                                    Weight = ObjectAssociationNode<Type>.WeightType.Parent
                                });
                                BuildGraph(gType);
                                //GenerateMockRepositories(gType);
                            }
                        }
                    }
                    else
                    {
                        this.Graph.Add(new ObjectAssociationNode<Type>()
                        {
                            TargetNode = t.PropertyType,
                            DestinationNode = type,
                            Weight = ObjectAssociationNode<Type>.WeightType.Parent
                        });
                        BuildGraph(t.PropertyType);
                    }
                  //  GenerateMockRepositories(t.PropertyType);
                }
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            if (textBox1.Text.Length == 0)
            {
                MessageBox.Show("Please select an assembly to auto generate classes from by clicking the [Find] button.");
                return;
            }

            if (listBox1.SelectedIndex == -1)
            {
                MessageBox.Show("Please make sure you select a type in the list box before attempting to auto generate classes for that type");
                return;
            }

            this.Graph = new ObjectAssociationGraph<Type>();
            this.FilesCreated = new List<string>();

            Type type = listBox1.SelectedItem as Type;
            BuildGraph(type);

            string rootDirectory = textBox2.Text.Replace("\\", "/").TrimEnd(new char[] { '/' }) + "/";

            var x = ParsedTemplate.GenerateMasterTemplate(Graph);
            x.GenerateIndividualEndpoints = true;
            x.GeneratedServiceInterfaceName = textBox5.Text;
            x.GeneratedMockServiceName = textBox4.Text;
            x.TargetNamespace = textBox3.Text;

            var t = x.GenerateSingleMockService();

            if (!Directory.Exists(rootDirectory))
                Directory.CreateDirectory(rootDirectory);

            if (mockServicesCheckbox.Checked)
                if (!Directory.Exists(rootDirectory + "Services"))
                    Directory.CreateDirectory(rootDirectory + "Mock Services");

            if (mockServicesCheckbox.Checked)
            {
                System.IO.File.WriteAllText(rootDirectory + "Mock Services/" + t.Name + ".cs", t.CSharpTemplate);
                t = x.GenerateServiceContract();
                System.IO.File.WriteAllText(rootDirectory + "Mock Services/" + t.Name + ".cs", t.CSharpTemplate);

                t = x.GenerateMockServiceUsingFakeRepository();
                System.IO.File.WriteAllText(rootDirectory + "Mock Services/" + t.Name + ".cs", t.CSharpTemplate);
            }

            if (dataContractCheckbox.Checked)
            {
                if (!Directory.Exists(rootDirectory + "Data Contracts"))
                    Directory.CreateDirectory(rootDirectory + "Data Contracts");

                foreach (var item in x.GenerateDataContracts())
                    System.IO.File.WriteAllText(rootDirectory + "Data Contracts/" + item.Name + ".cs", item.CSharpTemplate);
            }

            if (mockRepositoriesCheckbox.Checked)
            {
                if (!Directory.Exists(rootDirectory + "Mock Repositories"))
                    Directory.CreateDirectory(rootDirectory + "Mock Repositories");

                foreach (var item in x.GenerateMockRepositories())
                    System.IO.File.WriteAllText(rootDirectory + "Mock Repositories/" + item.Name + ".cs", item.CSharpTemplate);
            }

            if (mappingsFileCheckbox.Checked)
            {
                t = x.GenerateMappings();
                System.IO.File.WriteAllText(rootDirectory + t.Name + ".cs", t.CSharpTemplate);
            }


            t = x.GenerateGenericRepositoryInterface();
            System.IO.File.WriteAllText(rootDirectory + t.Name + ".cs", t.CSharpTemplate);

            t = x.GenerateGenericStringIdRepositoryInterface();
            System.IO.File.WriteAllText(rootDirectory + t.Name + ".cs", t.CSharpTemplate);


            System.Diagnostics.Process.Start(rootDirectory);

        }
    }

    public interface IRepository<T, keyId>
    {
        keyId Create(T item);
    }
    public interface IRepository<T> : IRepository<T, string>
    {
    }
}
